<p>Anteriormente titulado &quot;C&oacute;mo mezclar C con c&oacute;digo de ensamblador (assembly)&quot;</p>

<h2>1. Introducci&oacute;n.</h2>
<p>Existe una relaci&oacute;n muy estrecha entre la programaci&oacute;n en C y la programaci&oacute;n en ensamblador. C fue creado para evitarle a los programadores del sistema operativo UNIX seguir escribi&eacute;ndolo en ensamblador. Las ventajas de programar en C lo mismo que antes se programaba en ensamblador son obvias: portabilidad y legibilidad a trav&eacute;s de un c&oacute;digo bien estructurado. Sin embargo las partes del sistema en las que es necesario trabajar con caracter&iacute;sticas espec&iacute;ficas del hardware no pueden ser escritas en C.</p>
<p>En general hay dos estrategias para mezclar c&oacute;digo en C con c&oacute;digo en ensamblador. La primera consiste en compilar archivos escritos en C y ensamblar otros escritos en c&oacute;digo de ensamblador para luego enlazarlos todos juntos. La segunda se trata de embeber c&oacute;digo escrito en ensamblador dentro de c&oacute;digo escrito en C a trav&eacute;s de alg&uacute;n mecanismo que ofrezca el compilador usado. En este art&iacute;culo se tratar&aacute; s&oacute;lo la primera estrategia.</p>
<p>Aunque las ideas que aqu&iacute; se exponen son &uacute;tiles independientemente de cualquier plataforma, toda la nota presupone un sistema tipo UNIX (GNU en particular) en una arquitectura de procesador 80386 o superior.</p>
<h2>2. Construyendo programas.</h2>
<p>Dado un conjunto de archivos en C, es necesario seguir ciertos pasos para obtener uno ejecutable.</p>
<p>Para comenzar se deben incorporar los archivos de cabecera (los que se incluyeron mediante &quot;#include&quot;), expandir las macros y constantes (las que se declararon mediante &quot;#define&quot;) y quitar el c&oacute;digo que no satisfaga las condiciones para ser compilado (condiciones establecidas mediante &quot;#ifndef&quot;, &quot;#ifdef&quot; o alguna otra estructura condicional del preprocesador). Esto en C se conoce como &quot;preprocesar&quot; el c&oacute;digo fuente. En GNU/Linux los archivos &quot;preprocesados&quot; poseen la extensi&oacute;n &quot;.i&quot; y se generan con cualquiera de los siguientes comandos:</p>

<pre class="code">

$ cpp  ejemplo1.c &gt; ejemplo1.i

</pre>

o

<pre class="code">

$ cc -E ejemplo1.c &gt; ejemplo1.i

</pre>

<p>El pr&oacute;ximo paso consiste en traducir el c&oacute;digo en C preprocesado a c&oacute;digo en ensamblador. Esto se hace del siguiente modo:</p>
<pre class="code">

$ cc -S ejemplo1.i

</pre>
<p>Sin embargo tambi&eacute;n es posible obtener el c&oacute;digo en ensamblador a partir del archivo &quot;ejemplo1.c&quot;:</p>
<pre class="code">

$ cc -S ejemplo1.c

</pre>
<p>En este &uacute;ltimo caso el compilador preprocesar&aacute; el archivo autom&aacute;ticamente y al terminar la traducci&oacute;n eliminar&aacute; &quot;ejemplo1.i&quot;. De cualquier forma se obtendr&aacute; el mismo resultado, el archivo &quot;ejemplo1.s&quot;.</p>
<p>El tercer paso ser&aacute; ensamblar para conseguir c&oacute;digo de m&aacute;quina:</p>
<pre class="code">

$ as -o ejemplo1.o ejemplo1.s

</pre>
<p>Alternativamente se puede hacer con el comando</p>
<pre class="code">

$ cc -c ejemplo1.s

</pre>
<p>As&iacute; el compilador de C llamar&aacute; al ensamblador autom&aacute;ticamente.</p>
<p>Como resultado deber&iacute;amos obtener un archivo objeto con extensi&oacute;n &quot;.o&quot;. Los archivos objeto contienen c&oacute;digo de m&aacute;quina en el que las direcciones de memoria no han sido definidas a&uacute;n. Hay distintos formatos para estos archivos, entre ellos ELF (de Linux), COFF (algunos sistemas tipo UNIX), OMF (algunos sistemas de microchot).</p>
<p>Llegado a este punto debo decir que se podr&iacute;a haber obtenido el archivo &quot;ejemplo1.o&quot; directamente de &quot;ejemplo1.c&quot; de la siguiente forma:</p>
<pre class="code">

$ cc -c ejemplo1.c

</pre>
<p>Ahora s&oacute;lo resta enlazar todos los archivos objeto para obtener el archivo ejecutable &quot;programa-ejemplo&quot;:</p>
<pre class="code">

$ cc -o programa-ejemplo ejemplo1.o ejemplo2.o ejemplo3.o

</pre>
<p>Para hacer esto tambi&eacute;n es posible llamar al enlazador directamente con el comando "ld". Aunque no recomiendo hacerlo ya que es necesario pasarle demasiados par&aacute;metros. Para ver un ejemplo visite el enlace sugerido al final del art&iacute;culo al tutorial de gcc de V&iacute;ctor Alberto Gonz&aacute;lez Barbone.</p>
<h3>La compilaci&oacute;n separada.</h3>
<p>Note que podr&iacute;amos haber compilado todo junto con</p>
<pre class="code">

$ cc -o programa-ejemplo ejemplo1.c ejemplo2.c ejemplo3.c

</pre>
<p>Sin embargo compilar todo en un &uacute;nico paso no es una buena opci&oacute;n. Cada vez que cambiaremos algunos de los archivos habr&aacute; que volver a compilar todos nuevamente, y eso llevar&aacute; tiempo. Adem&aacute;s, si ocurriere un error la compilaci&oacute;n se suspender&aacute; perdiendo los resultados intermedios.</p>
<p>Para manejar la compilaci&oacute;n de varios archivos por separado la herramienta adecuada es el &quot;make&quot;. Explicar c&oacute;mo utilizarlo excede los l&iacute;mites de este art&iacute;culo. Nuevamente sugiero visitar los enlaces al final para m&aacute;s informaci&oacute;n.</p>

<h2>3. Mezclando c&oacute;digo en lenguaje C con c&oacute;digo en ensamblador.</h2>

<h3>Un ejemplo introductorio.</h3>
<p>Comenzar&eacute; mostrando un in&uacute;til programa que recibe como par&aacute;metro un n&uacute;mero real y lo redondea. Aunque no entienda muy bien el c&oacute;digo, no se preocupe, lo importante ahora es que comprenda la estructura.</p>
<p>Se trata de tres archivos: &quot;main.c&quot;, &quot;redon.s&quot; y &quot;redon.h&quot;. El primero es el que contiene la funci&oacute;n &quot;main&quot; escrita en C, ah&iacute; es donde comienza la ejecuci&oacute;n. El segundo contiene la funci&oacute;n &quot;redon&quot;, que recibe como par&aacute;metro un n&uacute;mero flotante, lo redondea y lo convierte en entero. En el tercero est&aacute; el prototipo de &quot;redon&quot;, necesario para que la funci&oacute;n pueda ser llamada desde otros archivos.</p>
<p>El archivo &quot;main.c&quot;:</p>
<pre class="code">

#include &quot;redon.h&quot;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int
main (int argc, char *argv [])
{
   float   n;
   int     n_redon;

   if (2 != argc) {
       fprintf (stderr, &quot;Error: debe haber un par&aacute;metro... aunque no m&aacute;s.\n&quot;);
       return 1;
   }

   n = (float) atof (argv [1]);
   n_redon = redon (n);

   printf (&quot;El n&uacute;mero entero que m&aacute;s se aproxima a %f es %d.\n&quot;, n, n_redon);

   return 0;
}

</pre>
<p>El archivo &quot;redon.s&quot;:</p>
<pre class="code">

.text

.globl redon
redon:
   flds        0x04 (%esp)
   fistpl      0x04 (%esp)
   movl        0x04 (%esp), %eax
   ret

</pre>
<p>El archivo &quot;redon.h&quot;:</p>
<pre class="code">

#ifndef REDON_H
#define REDON_H

int redon (float);

#endif /* REDON_H */

</pre>
<p>Observe el uso de las directivas &quot;.text&quot; y &quot;.globl&quot;. La primera, aunque no parezca, indica que lo que sigue es c&oacute;digo. La segunda, que &quot;redon&quot; podr&aacute; ser llamada desde funciones que est&eacute;n en otros archivos objeto. Esto contrasta con las funciones en C, las cuales son globales por defecto a no ser que se indique lo contrario mediante la palabra clave &quot;static&quot;.</p>
<p>Para compilar y enlazar todo se deben ejecutar los siguientes comandos:</p>
<pre class="code">

$ cc -Wall -O -c main.c
$ cc -c redon.s
$ cc -o redondear main.o redon.o

</pre>
<h3>Las tres convenciones.</h3>
<p>Para escribir funciones en ensamblador que puedan ser llamadas desde otras en C debe conocer tres convenciones. La primera tiene que ver con el pasaje de par&aacute;metros, la segunda con el retorno de valores y la tercera con la preservaci&oacute;n de los valores en los registros.</p>
<h4>Pasaje de par&aacute;metros</h4>
<p>Cuando un programa se ejecuta el sistema operativo le asigna memoria para que pueda operar. Parte de la memoria asignada al programa es utilizada como una pila para almacenar las variables locales a las funciones (a excepci&oacute;n de las declaradas con la palabra clave &quot;static&quot;), los par&aacute;metros y algunas cosas m&aacute;s.</p>
<p>En una arquitectura 80386 el registro &quot;esp&quot; (puntero de pila) es el que se encarga de guardar un puntero hacia el tope de la pila. Cuando se insertan datos en la pila a trav&eacute;s de la instrucci&oacute;n &quot;push&quot; el tope de la pila ya no es el mismo, entonces el puntero de pila (que est&aacute; guardado en el registro &quot;esp&quot;) es cambiado autom&aacute;ticamente para que pueda continuar apuntando hacia el tope. Lo inverso ocurre cuando se quitan datos de la pila a trav&eacute;s de la instrucci&oacute;n &quot;pop&quot;. Cabe recordar que en una pila no es posible insertar o quitar datos de otro lugar que no sea su tope, de lo contrario no ser&iacute;a una pila.</p>
<p>Si no est&aacute; familiarizado con lo que estoy diciendo, probablemente tampoco est&eacute; familiarizado con la programaci&oacute;n en ensamblador. Entonces ser&iacute;a muy conveniente que lea alg&uacute;n apunte introductorio acerca del tema. En Internet debe haber alguno corto. La programaci&oacute;n en ensamblador excede los l&iacute;mites de este humilde art&iacute;culo.</p>
<p>Volviendo al punto, la importancia de la pila aqu&iacute; es que es el mecanismo mediante el cual se pasan los par&aacute;metros a las funciones. Los par&aacute;metros son pasados de derecha a izquierda, es decir, primero se inserta en la pila el &uacute;ltimo par&aacute;metro, luego el pen&uacute;ltimo, y as&iacute; se contin&uacute;a hasta insertar finalmente el primero. La funci&oacute;n que llama a otra es la que se encarga de colocar los par&aacute;metros en la pila y tambi&eacute;n de quitarlos. Esta es la convenci&oacute;n en C, aunque existen otras convenciones en otros lenguajes en las cuales es la funci&oacute;n llamada la que quita los par&aacute;metros de la pila.</p>
<p>En un intento de aclarar el tema dir&eacute; que para invocar a la funci&oacute;n</p>
<pre class="code">

void f (int param1, int param2, int param3);

</pre>
<p>con param1 = 3, param2 = 2 y param3 = 5 se deber&iacute;a escribir el siguiente c&oacute;digo en C:</p>
<pre class="code">

f (3, 2, 5);

</pre>
<p>y el siguiente en ensamblador:</p>
<pre class="code">

pushl   $5
pushl   $2
pushl   $3
call    f
addl    $12, %esp

</pre>
<p>Las tres primeras instrucciones colocan los par&aacute;metros en la pila. La cuarta llama a la funci&oacute;n &quot;f&quot; y la quinta le adiciona doce al puntero de pila. Esto es porque los par&aacute;metros insertados ocupan doce bytes entre los tres (cuatro cada uno).</p>
<p>Adicionarle y sustraerle al puntero de pila son maneras de liberar espacio y de hacer espacio en la pila respectivamente.</p>
<p>Notar&aacute; que luego de &quot;push&quot; y de &quot;add&quot; hay una &quot;l&quot;. Esto no es propio del lenguaje ensamblador 80x86 de Intel. Sino que pertenece a la notaci&oacute;n de AT&T, que es la adoptada por el ensamblador de GNU. Esta &quot;l&quot; significa que el par&aacute;metro de la instrucci&oacute;n es de 32 bits.</p>
<h4>Retorno de valores.</h4>
<p>Seg&uacute;n el tipo del valor retornado se procede de distinto modo.</p>
<p>Para los tipos de datos predefinidos por el lenguaje la funci&oacute;n llamada deposita el valor retornado en determinado registro. Luego la funci&oacute;n que ha realizado el llamado asumir&aacute; que est&aacute; en el registro convenido y acceder&aacute; a &eacute;l para obtenerlo. El registro que se utiliza depende del tipo de dato del valor retornado.</p>
<p>A continuaci&oacute;n listar&eacute; distintos tipos de datos y los registros utilizados.</p>

<table border="2">
<thead>
<tr>
<th>Tipo de variable</th>
<th>Registro</th>
</tr>
</thead>
<tbody>
<tr>
<td>int / unsigned int / long / unsigned long</td>
<td>eax</td>
</tr>
<tr>
<td>short / unsigned short</td>
<td>ax</td>
</tr>
<tr>
<td>char / unsigned char</td>
<td>al</td>
</tr>
<tr>
<td>punteros</td>
<td>eax</td>
</tr>
<tr>
<td>float / double</td>
<td>st (0)</td>
</tr>
</tbody>
</table>

<p>Las funciones que devuelven un tipo de dato definido por el usuario mediante &quot;struct&quot; o &quot;union&quot; cuentan autom&aacute;ticamente con un primer par&aacute;metro impl&iacute;cito adicional que indica la direcci&oacute;n de memoria en donde ser&aacute; copiado el valor devuelto.</p>
<p>Por lo tanto se podr&iacute;a decir que las funciones:</p>
<pre class="code">

struct s f (int param1, int param2, int param3);

</pre>
<p>y</p>
<pre class="code">

void f (struct s *retval, int param1, int param2, int param3);

</pre>
<p>una vez traducidas al ensamblador tienen los mismos par&aacute;metros.</p>
<p>Los tipos de datos definidos con &quot;enum&quot; se implementan del mismo modo que un numero entero, por lo tanto se devuelven copiando el valor al registro &quot;eax&quot;.</p>
<h4>Preservaci&oacute;n de los valores de los registros.</h4>
<p>En el momento de retorno de una funci&oacute;n se espera que el valor de ciertos registros sea el mismo que en el momento de su llamado. Estos registros son ebp, esp, ebx, esi y edi. Por lo tanto si la funci&oacute;n har&aacute; uso de ellos deber&aacute; guardarlos previamente y restaurarlos a sus valores originales luego, o asegurarse de cualquier otra forma que queden como los puso la funci&oacute;n que hizo el llamado.</p>
<p>Los registros eax, ecx, edx, eflags y los de la unidad de punto flotante (st (0), st (1), st (2), ..., st (7) y los registros de control y estado) pueden ser utilizados sin cuidado alguno. El resto de los registros del procesador (que no enumerar&eacute;) s&oacute;lo puede ser modificado por el sistema operativo, por lo tanto se descarta su posibilidad de uso.</p>

<h2>4. Algunos consejos.</h2>

<p>Llegando ya al final me gustar&iacute;a mencionar algunas sugerencias.</p>
<h3>El compilador de C es una buena herramienta para estudiar ensamblador.</h3>
<p>Escribir funciones en C y utilizar el compilador para traducirlas al ensamblador es una excelente manera de conseguir ejemplos de rutinas en este lenguaje. Deber&iacute;a ser uno de los primeros recursos para eliminar todas las dudas que surjan.</p>
<p>Es aconsejable hacerlo activando la opci&oacute;n de optimizaci&oacute;n para obtener un buen c&oacute;digo y las advertencias para detectar errores. El comando para hacer esto es el siguiente:</p>
<pre class="code">

$ cc -Wall -O -S ejemplo.c

</pre>

<h3>Cuidado con el &quot;decorado de nombres&quot; en C++ y en algunos sistemas operativos.</h3>
<p>Es muy probable que algunos, tras leer este art&iacute;culo, intenten mezclar ensamblador con C++ y las cosas no funcionen. Este lenguaje posee una propiedad denominada &quot;decorado de nombre&quot; (name decoration) o &quot;planchado de nombre&quot; (name mangling) que consiste en cambiar el nombre de las funciones cuando son traducidas al ensamblador. C++ necesita hacer esto para soportar la sobrecarga de funciones, las funciones miembro y otras caracter&iacute;sticas del lenguaje. Por lo tanto esperar&aacute; que todos los nombres hayan sido decorados adecuadamente.</p>
<p>A pesar de esto es posible invocar funciones cuyo nombre no ha sido &quot;decorado&quot; encerrando sus prototipos en un bloque precedido por la frase &quot;extern &quot;C&quot; &quot;.</p>
<p>Para ilustrar con un ejemplo, el archivo &quot;redon.h&quot;, para poder ser usado en C y tambi&eacute;n en C++, deber&iacute;a escribirse as&iacute;:</p>
<pre class="code">

#ifndef REDON_H
#define REDON_H

#ifdef __cplusplus
extern &quot;C&quot; {
#endif

int redon (float);

#ifdef __cplusplus
}
#endif

#endif /* REDON_H */

</pre>

<p>El compilador de C en un popular sistema operativo no POSIX propenso a fallas realiza tambi&eacute;n un decorado seg&uacute;n la convenci&oacute;n de llamada de la funci&oacute;n. Sin embargo consiste s&oacute;lo en agregarle un gui&oacute;n bajo al nombre. As&iacute;, por ejemplo, el archivo &quot;redon.s&quot; deber&iacute;a sustituirse por el siguiente:</p>
<pre class="code">

.text

.globl _redon
_redon:
   flds        0x04 (%esp)
   fistpl      0x04 (%esp)
   movl        0x04 (%esp), %eax
   ret

</pre>
<p>Pero &quot;redon.h&quot; no deber&iacute;a ser modificado.</p>
<p>Para una mejor explicaci&oacute;n del asunto se recomienda visitar los enlaces al final del art&iacute;culo.</p>
<h3>Utilice el comando &quot;objdump&quot; para desensamblar archivos binarios.</h3>
<p>Este comando propio del UNIX desensambla archivos objeto y archivos ejecutables.</p>
<p>Para hacerlo debe invocar el comando con la opci&oacute;n &quot;-d&quot;:</p>
<pre class="code">

$ objdump -d ejemplo.o

</pre>
<p>Consulte la documentaci&oacute;n del sistema operativo para ver otras opciones que acepta el comando.</p>
<h3>Utilice el comando &quot;nm&quot; para listar las funciones en un archivo binario.</h3>
<p>En los sistemas tipo UNIX el comando &quot;nm&quot; lista las funciones y variables que est&aacute;n dentro de un archivo objeto o un archivo ejecutable y a las que hacen referencia funciones dentro del archivo.</p>
<p>Puede utilizarlo del siguiente modo:</p>
<pre class="code">

$ nm ejemplo.o

</pre>
<p>Para aprender a leer el listado que genera sugiero recurrir a la documentaci&oacute;n del comando.</p>
<h2>5. Enlaces que se sugiere visitar.</h2>

<ol>
  <li>
    <a href="http://www.hackemate.com.ar/textos/papers/Gcc%20Inline%20Assembly%20-%20How%20to%20-%20By%20Astor/inline.html">
      http://www.hackemate.com.ar/textos/papers/Gcc%20Inline%20Assembly%20-%20How%20to%20-%20By%20Astor/inline.html
    </a>
    <br />
    Art&iacute;culo de Mart&iacute;n Candurra (astor) acerca del Inline Assembly en GCC.
  </li>
  <li>
    <a href="http://iie.fing.edu.uy/%7Evagonbar/gcc-make/gcc.htm">
      http://iie.fing.edu.uy/%7Evagonbar/gcc-make/gcc.htm
    </a>
    <br />
    Breve tutorial en espa&ntilde;ol acerca del gcc de V&iacute;ctor Alberto Gonz&aacute;lez Barbone.
  </li>
  <li>
    <a href="http://en.wikipedia.org/wiki/Name_mangling">
      http://en.wikipedia.org/wiki/Name_mangling
    </a>
    <br />
    Art&iacute;culo de la Wikipedia en ingl&eacute;s que explica con detalle de qu&eacute; se trata el planchado de nombres.
  </li>
  <li>
    <a href="http://sig9.com/articles/att-syntax">
      http://sig9.com/articles/att-syntax
    </a>
    <br />
    Art&iacute;culo en ingl&eacute;s de &quot;vivek&quot; que introduce la sintaxis de AT&amp;T.
  </li>
  <li>
    <a href="http://www.website.masmforum.com/tutorials/fptute">
      http://www.website.masmforum.com/tutorials/fptute
    </a>
    <br />
    &quot;Simply FPU&quot; es un tutorial escrito por Raymond Filiatreault que ense&ntilde;a a usar la unidad de punto flotante (FPU).
  </li>
</ol>
